namespace FIREFLYX {
    /** 着色器code依赖包含 类型 */
    enum IncludeType {
        /** 引擎内建库 */
        LIB,
        LINK
    }
    /** glsl 片段资源 
     * 
     * 添加include 拓展
     * #pragma include "./xxx.glsl"
     * 参考 https://github1s.com/akira-cn/gl-renderer/blob/HEAD/src/renderer.js (line : 605)
    */
    export class GLSL extends Asset {
        public filePath: string = "";

        // private static readonly IncludeRegStr = `^ *#pragma *include\\s+.*`;
        private static readonly IncludeLibRegStr = `^ *#pragma +include +<.*>`;
        private static readonly IncludeLinkRegStr = `^ *#pragma +include +".*"`;
        private _code: string;
        private _isLoadAll: boolean = false;
        private _includeMap: { [key: string]: [IncludeType, string] };
        private _needFullParse: boolean;

        /** 是否依赖资源全部加载完毕 */
        public isLoadedAll(): boolean {
            return this._isLoadAll;
        }

        /** 加载所有依赖的资源 */
        public async loadAllFile() {
            if (this._isLoadAll) return;
            if (!this._code) {
                console.error(`can't to load ,_code is null.`);
                return;
            }
            const regMg = new RegExp(GLSL.IncludeLinkRegStr, `mg`);
            const matched = this._code.match(regMg);
            //
            if (!matched) {
                this._isLoadAll = true;
                return;
            }

            let _idx = this.filePath.lastIndexOf("/");
            let folderPath = `${this.filePath.substring(0, _idx)}/`;

            //遍历加载 include 片段
            let allFile: Promise<GLSL>[] = [];
            for (let i = 0, len = matched.length; i < len; i++) {
                const m = matched[i];
                const _matched = m.match(/(?:")(.*)(?:")/);
                if (!_matched) continue;
                // const _type = _matched[0].indexOf("<") === 0 ? IncludeType.LIB : IncludeType.LINK;
                let content = _matched[1];  //内容主体
                //检查是否重复
                if (!this._includeMap[content]) {
                    this._includeMap[content] = [IncludeType.LINK, m];
                } else {
                    console.warn(`has repeat include of "${m}".`);
                }

                //加载
                //检查 指定content的资源已经被加载过了
                let fUrl = `${folderPath}${content}`;
                if (!AssetManager.GetAsset(fUrl)) { allFile.push(AssetManager.LoadFile(fUrl) as any); }
            }

            if (allFile.length <= 0) {
                this._isLoadAll = true;
                return;
            }

            //等待所有载完
            let glsls = await Promise.all(allFile)
                .catch((reason) => {
                    console.error(`include fail , not loaded. \n reason : ${reason}`);
                });

            //load all
            let allLoad: Promise<any>[] = [];
            if (glsls && glsls.length > 0) {
                for (let i = 0, len = glsls.length; i < len; i++) {
                    let _g = glsls[i];
                    if (_g.isLoadedAll()) continue;
                    allLoad.push(_g.loadAllFile());
                }
            }

            //等加载
            await Promise.all(allLoad);

            //标记 载完
            this._isLoadAll = true;
        }

        /**
         * 设置 glsl片段代码文本
         * @param code glsl片段代码文本
         */
        public setCode(code: string) {
            if (!code) return;
            if (this._code) {
                //只让设置一次
                console.warn(`GLSL only can setCode once.`);
                return;
            }
            this._needFullParse = true;
            this._code = code;
            // const hasInc = new RegExp(GLSL.IncludeRegStr, `mg`).test(this._code);
            const regLib = new RegExp(GLSL.IncludeLibRegStr, `mg`);
            const hasLib = regLib.test(this._code);
            const hasLink = new RegExp(GLSL.IncludeLinkRegStr, `mg`).test(this._code);
            this._isLoadAll = !hasLink;
            this._includeMap = hasLib || hasLink ? {} as any : null;
            //填入 lib
            if (hasLib) {
                const matched = this._code.match(regLib) as RegExpMatchArray;
                for (let i = 0, len = matched.length; i < len; i++) {
                    const m = matched[i];
                    const _matched = m.match(/(?:<)(.*)(?:>)/);
                    if (!_matched) continue;
                    let content = _matched[1];  //内容主体
                    //检查是否重复
                    if (!this._includeMap[content]) {
                        this._includeMap[content] = [IncludeType.LIB, m];
                    } else {
                        console.warn(`has repeat include of "${m}".`);
                    }
                }
            }
            // console.log(`code:${this._code}`);
        }

        /** 获取完整的glsl代码文本(合并过依赖库的) */
        public getFull() {
            if (!this._code) {
                console.error(`code is empty , need call "setCode" to set code.`);
                return null;
            }
            if (!this._isLoadAll) {
                console.error(`have include dependent not load , need call "loadAllFile".`);
                return null;
            }
            if (!this._includeMap || !this._needFullParse) return this._code;  //没有依赖
            //组合依赖
            let _fullCode = this._code;
            let _idx = this.filePath.lastIndexOf("/");
            let folderPath = `${this.filePath.substring(0, _idx)}/`;
            const keys = Object.keys(this._includeMap);
            for (let i = 0, len = keys.length; i < len; i++) {
                const k = keys[i];
                if (!this._includeMap[k]) { continue; }
                const inc = this._includeMap[k];
                let _glsl: Nullable<GLSL> = null;
                let msg = "";
                switch (inc[0]) {
                    case IncludeType.LIB:
                        _glsl = DefaultAsset.getGLSLLib(k);
                        if (!_glsl) { msg = `glsl lib <${k}> not find.`; }
                        break;
                    case IncludeType.LINK:
                        let fUrl = `${folderPath}${k}`;
                        _glsl = AssetManager.GetAsset(fUrl) as GLSL;
                        if (!_glsl) { msg = `file "${fUrl}" not find.`; }
                        break;
                    default:
                }

                let _incStr = "";
                if (_glsl) {
                    _incStr = _glsl.getFull() ?? _incStr;
                } else {
                    console.warn(msg);
                }

                //替换掉 include 字符串
                _fullCode = _fullCode.replace(inc[1], _incStr);
            }

            this._needFullParse = false;    //parse 完了不再需要Parse
            this._code = _fullCode;

            return _fullCode;
        }
    }
}